[asan] Make vector asan annotations exception-friendly Fix vector asan annotations with RAII. Add a test. Also, remove one dead function. Review: http://reviews.llvm.org/D4170 git-svn-id: https://llvm.org/svn/llvm-project/libcxx/trunk@216995 91177308-0d34-0410-b5e6-96231b3b80d8 
diff --git a/include/vector b/include/vector index 1be584d..12195df 100644 --- a/include/vector +++ b/include/vector 
@@ -784,7 +784,6 @@  void  >::type  __construct_at_end(_ForwardIterator __first, _ForwardIterator __last); - void __move_construct_at_end(pointer __first, pointer __last);  void __append(size_type __n);  void __append(size_type __n, const_reference __x);  _LIBCPP_INLINE_VISIBILITY @@ -836,7 +835,7 @@  // may not meet the AddressSanitizer alignment constraints.  // See the documentation for __sanitizer_annotate_contiguous_container for more details.  void __annotate_contiguous_container - (const void *__beg, const void *__end, const void *__old_mid, const void *__new_mid) + (const void *__beg, const void *__end, const void *__old_mid, const void *__new_mid) const  {  #ifndef _LIBCPP_HAS_NO_ASAN  if (__beg && is_same<allocator_type, __default_allocator_type>::value) @@ -844,26 +843,42 @@  #endif  }   - void __annotate_new(size_type __current_size) + void __annotate_new(size_type __current_size) const  {  __annotate_contiguous_container(data(), data() + capacity(),  data() + capacity(), data() + __current_size);  } - void __annotate_delete() + void __annotate_delete() const  {  __annotate_contiguous_container(data(), data() + capacity(),  data() + size(), data() + capacity());  } - void __annotate_increase(size_type __n) + void __annotate_increase(size_type __n) const  {  __annotate_contiguous_container(data(), data() + capacity(),  data() + size(), data() + size() + __n);  } - void __annotate_shrink(size_type __old_size) + void __annotate_shrink(size_type __old_size) const  {  __annotate_contiguous_container(data(), data() + capacity(),  data() + __old_size, data() + size());  } + // The annotation for size increase should happen before the actual increase, + // but if an exception is thrown after that the annotation has to be undone. + struct __RAII_IncreaseAnnotator { + __RAII_IncreaseAnnotator(const vector &__v, size_type __n = 1) + : __commit(false), __v(__v), __n(__n) { + __v.__annotate_increase(__n); + } + void __done() { __commit = true; } + ~__RAII_IncreaseAnnotator() { + if (__commit) return; + __v.__annotate_shrink(__v.size() + __n); + } + bool __commit; + size_type __n; + const vector &__v; + };  };    template <class _Tp, class _Allocator> @@ -959,12 +974,13 @@  vector<_Tp, _Allocator>::__construct_at_end(size_type __n)  {  allocator_type& __a = this->__alloc(); - __annotate_increase(__n);  do  { + __RAII_IncreaseAnnotator __annotator(*this);  __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_));  ++this->__end_;  --__n; + __annotator.__done();  } while (__n > 0);  }   @@ -980,12 +996,13 @@  vector<_Tp, _Allocator>::__construct_at_end(size_type __n, const_reference __x)  {  allocator_type& __a = this->__alloc(); - __annotate_increase(__n);  do  { + __RAII_IncreaseAnnotator __annotator(*this);  __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_), __x);  ++this->__end_;  --__n; + __annotator.__done();  } while (__n > 0);  }   @@ -1001,22 +1018,9 @@  allocator_type& __a = this->__alloc();  for (; __first != __last; ++__first)  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_), *__first); - ++this->__end_; - } -} - -template <class _Tp, class _Allocator> -void -vector<_Tp, _Allocator>::__move_construct_at_end(pointer __first, pointer __last) -{ - allocator_type& __a = this->__alloc(); - for (; __first != __last; ++__first) - { - __annotate_increase(1); - __alloc_traits::construct(__a, _VSTD::__to_raw_pointer(this->__end_), - _VSTD::move(*__first)); + __annotator.__done();  ++this->__end_;  }  } @@ -1578,9 +1582,10 @@  {  if (this->__end_ != this->__end_cap())  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  __alloc_traits::construct(this->__alloc(),  _VSTD::__to_raw_pointer(this->__end_), __x); + __annotator.__done();  ++this->__end_;  }  else @@ -1596,10 +1601,11 @@  {  if (this->__end_ < this->__end_cap())  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  __alloc_traits::construct(this->__alloc(),  _VSTD::__to_raw_pointer(this->__end_),  _VSTD::move(__x)); + __annotator.__done();  ++this->__end_;  }  else @@ -1629,10 +1635,11 @@  {  if (this->__end_ < this->__end_cap())  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  __alloc_traits::construct(this->__alloc(),  _VSTD::__to_raw_pointer(this->__end_),  _VSTD::forward<_Args>(__args)...); + __annotator.__done();  ++this->__end_;  }  else @@ -1712,7 +1719,7 @@  pointer __p = this->__begin_ + (__position - begin());  if (this->__end_ < this->__end_cap())  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  if (__p == this->__end_)  {  __alloc_traits::construct(this->__alloc(), @@ -1727,6 +1734,7 @@  ++__xr;  *__p = *__xr;  } + __annotator.__done();  }  else  { @@ -1752,7 +1760,7 @@  pointer __p = this->__begin_ + (__position - begin());  if (this->__end_ < this->__end_cap())  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  if (__p == this->__end_)  {  __alloc_traits::construct(this->__alloc(), @@ -1765,6 +1773,7 @@  __move_range(__p, this->__end_, __p + 1);  *__p = _VSTD::move(__x);  } + __annotator.__done();  }  else  { @@ -1791,7 +1800,7 @@  pointer __p = this->__begin_ + (__position - begin());  if (this->__end_ < this->__end_cap())  { - __annotate_increase(1); + __RAII_IncreaseAnnotator __annotator(*this);  if (__p == this->__end_)  {  __alloc_traits::construct(this->__alloc(), @@ -1805,6 +1814,7 @@  __move_range(__p, this->__end_, __p + 1);  *__p = _VSTD::move(__tmp);  } + __annotator.__done();  }  else  { @@ -1843,8 +1853,9 @@  }  if (__n > 0)  { - __annotate_increase(__n); + __RAII_IncreaseAnnotator __annotator(*this);  __move_range(__p, __old_last, __p + __old_n); + __annotator.__done();  const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);  if (__p <= __xr && __xr < this->__end_)  __xr += __old_n; @@ -1954,8 +1965,9 @@  }  if (__n > 0)  { - __annotate_increase(__n); + __RAII_IncreaseAnnotator __annotator(*this, __n);  __move_range(__p, __old_last, __p + __old_n); + __annotator.__done();  _VSTD::copy(__first, __m, __p);  }  } 
diff --git a/test/containers/sequences/vector/asan_throw.pass.cc b/test/containers/sequences/vector/asan_throw.pass.cc new file mode 100644 index 0000000..a1dce4a --- /dev/null +++ b/test/containers/sequences/vector/asan_throw.pass.cc 
@@ -0,0 +1,198 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// Test asan vector annotations with a class that throws in a CTOR. + +#include <vector> +#include <cassert> + +#include "asan_testing.h" + +class X { +public: + X(const X &x) { Init(x.a); } + X(char arg) { Init(arg); } + X() { Init(42); } + X &operator=(const X &x) { + Init(x.a); + return *this; + } + void Init(char arg) { + if (arg == 42) + throw 0; + if (arg == 66) + arg = 42; + a = arg; + } + char get() const { return a; } + void set(char arg) { a = arg; } + +private: + char a; +}; + +void test_push_back() { + std::vector<X> v; + v.reserve(2); + v.push_back(X(2)); + assert(v.size() == 1); + try { + v.push_back(X(66)); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_emplace_back() { +#ifndef _LIBCPP_HAS_NO_VARIADICS + std::vector<X> v; + v.reserve(2); + v.push_back(X(2)); + assert(v.size() == 1); + try { + v.emplace_back(42); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +#endif // _LIBCPP_HAS_NO_VARIADICS +} + +void test_insert_range() { + std::vector<X> v; + v.reserve(4); + v.push_back(X(1)); + v.push_back(X(2)); + assert(v.size() == 2); + assert(v.capacity() >= 4); + try { + char a[2] = {21, 42}; + v.insert(v.end(), a, a + 2); + assert(0); + } catch (int e) { + assert(v.size() == 3); + } + assert(v.size() == 3); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_insert() { + std::vector<X> v; + v.reserve(3); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + try { + v.insert(v.end(), X(66)); + assert(0); + } catch (int e) { + assert(v.size() == 2); + } + assert(v.size() == 2); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_emplace() { +#ifndef _LIBCPP_HAS_NO_VARIADICS + std::vector<X> v; + v.reserve(3); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + try { + v.emplace(v.end(), 42); + assert(0); + } catch (int e) { + assert(v.size() == 2); + } + assert(v.size() == 2); + assert(is_contiguous_container_asan_correct(v)); +#endif // _LIBCPP_HAS_NO_VARIADICS +} + +void test_insert_range2() { + std::vector<X> v; + v.reserve(4); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + assert(v.capacity() >= 4); + try { + char a[2] = {10, 42}; + v.insert(v.begin(), a, a + 2); + assert(0); + } catch (int e) { + assert(v.size() <= 4); + assert(is_contiguous_container_asan_correct(v)); + return; + } + assert(0); +} + +void test_insert_n() { + std::vector<X> v; + v.reserve(10); + v.insert(v.end(), X(1)); + v.insert(v.begin(), X(2)); + assert(v.size() == 2); + try { + v.insert(v.begin(), 1, X(66)); + assert(0); + } catch (int e) { + assert(v.size() <= 3); + assert(is_contiguous_container_asan_correct(v)); + return; + } + assert(0); +} + +void test_resize() { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0)); + try { + v.resize(3); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +} + +void test_resize_param() { + std::vector<X> v; + v.reserve(3); + v.push_back(X(0)); + try { + v.resize(3, X(66)); + assert(0); + } catch (int e) { + assert(v.size() == 1); + } + assert(v.size() == 1); + assert(is_contiguous_container_asan_correct(v)); +} + +int main() { + test_push_back(); + test_emplace_back(); + test_insert_range(); + test_insert(); + test_emplace(); + test_insert_range2(); + test_insert_n(); + test_resize(); + test_resize_param(); +} 
diff --git a/test/support/asan_testing.h b/test/support/asan_testing.h index c8797bd..45ad04b 100644 --- a/test/support/asan_testing.h +++ b/test/support/asan_testing.h 
@@ -19,7 +19,7 @@  template <typename T, typename Alloc>  bool is_contiguous_container_asan_correct ( const std::vector<T, Alloc> &c )  { - if ( std::is_same<Alloc, std::allocator<T>>::value && c.data() != NULL) + if ( std::is_same<Alloc, std::allocator<T> >::value && c.data() != NULL)  return __sanitizer_verify_contiguous_container (  c.data(), c.data() + c.size(), c.data() + c.capacity()) != 0;  return true; @@ -34,4 +34,4 @@  #endif     -#endif // ASAN_TESTING_H \ No newline at end of file +#endif // ASAN_TESTING_H